/*
 *  Created at 2011-12-07 21:37 GMT-03:00
 *  Author: Victor Williams Stafusa da Silva
 *  JSmartUnit
 */
package org.jsmartunit.invocations;

import java.lang.reflect.Method;

import java.util.ArrayList;
import java.util.List;

import org.jsmartunit.model.InstanceFactory;
import org.jsmartunit.model.TestRunner;
import org.jsmartunit.statement.NotifyIgnoreRunner;
import org.jsmartunit.statement.RunBeforesAfters;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

/**
 * @author Victor Williams Stafusa da Silva
 */
public class ClassInvocation implements Invocation {

    private final RunNotifier notifier;
    private final Class<?> testClass;
    private final String prefix;

    public ClassInvocation(String prefix, RunNotifier notifier, Class<?> testClass) {
        if (prefix == null) throw new IllegalArgumentException();
        if (notifier == null) throw new IllegalArgumentException();
        if (testClass == null) throw new IllegalArgumentException();

        this.prefix = prefix;
        this.notifier = notifier;
        this.testClass = testClass;
    }

    public TestRunner createRunner() throws InitializationError {
        TestRunner runner = new TestRunner(prefix + testClass.getName(), testClass.getAnnotations());
        runner.addChildren(addTestMethodChildren());
        if (runner.getOrderedChildren().isEmpty()) throw new InitializationError("No runnable methods");

        Statement rec = runner.getChildrenRunnerStatement();
        rec = RunBeforesAfters.aroundClass(rec, testClass);

        runner.setStatement(rec);

        return runner;
    }

    private List<TestRunner> addTestMethodChildren() {
        List<TestRunner> runners = new ArrayList<TestRunner>(50);

        for (Method m : testClass.getMethods()) {
            if (!m.isAnnotationPresent(Test.class)) continue;

            if (m.isAnnotationPresent(Ignore.class)) {
                TestRunner methodRunner = new TestRunner(m.getName(), m.getAnnotations());
                Statement rec = NotifyIgnoreRunner.ignorerFor(methodRunner, notifier);
                methodRunner.setStatement(rec);
                runners.add(methodRunner);
                continue;
            }

            MethodInvocationArgumentBuilder builder =
                    new MethodInvocationArgumentBuilder(prefix, notifier, InstanceFactory.Factories.creator(testClass), m);

            runners.addAll(builder.runnersFor());
        }

        return runners;
    }
}
